using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Reflection;
using System.Drawing;
using System.Globalization;

namespace Camalon.Drawing
{
	public class ExtendedColorConverter:TypeConverter
	{


		public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
		{
			if (destinationType == typeof(string)) return true;
			if (destinationType == typeof(InstanceDescriptor)) return true;

			

			return base.CanConvertTo (context, destinationType);
		}

		public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
		{
			if (sourceType == typeof(string)) return true;
			if (sourceType == typeof(InstanceDescriptor)) return true;

			return base.CanConvertFrom (context, sourceType);
		}

		public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
		{

			if( value is string) return FromColorString( (string)value);

			return base.ConvertFrom (context, culture, value);

		}


		public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
		{
			if( (value is TransparentColor)|| ( value is Color ))
			{

				Color m;
			
				if( value is Color) m = (Color)value;
				else m = ((TransparentColor)value).Color;

				if (destinationType == typeof(string)) 
				{
					string colorString = ToColorString( m );

					if( colorString.Length == 0) 
						throw base.GetConvertToException( context.PropertyDescriptor.GetValue(context.Instance), destinationType);

					return colorString;

				}


				if (destinationType == typeof(InstanceDescriptor) ) 
				{
					if( value is TransparentColor)
					{
						ConstructorInfo ctor = typeof(TransparentColor).GetConstructor(new Type[] {typeof(Color)});
						if (ctor != null) return new InstanceDescriptor(ctor, new object[] {m});
					}
					else
					{
						System.Drawing.ColorConverter cc = new System.Drawing.ColorConverter();
						return cc.ConvertTo( context, culture, value, destinationType);
					}
				}
			}

			return base.ConvertTo (context, culture, value, destinationType);

		}

		private static ArrayList	_webColors		= GetColorList( true );
		private static ArrayList	_systemColors	= GetColorList( false);

		private static ArrayList GetColorList( bool web )
		{
			PropertyInfo[]	colorProp ;

			if( web)
				colorProp = typeof (Color).GetProperties (BindingFlags.Public | BindingFlags.Static);
			else
				colorProp = typeof (SystemColors).GetProperties (BindingFlags.Public | BindingFlags.Static);
	
			ArrayList colors = new ArrayList( );

			foreach (PropertyInfo prop in colorProp) 
			{
				Color color = (Color) prop.GetValue (null, null);
				colors.Add( color );
			}

			if( web) colors.Sort (new ColorSorter ());
			return colors;
		}


		protected internal static string UNKNOWN_COLOR_TEXT = "New Color";

		private static bool IsTransparent( Color color )
		{
			return color.A == 0 && color.R==255 && color.G == 255 && color.B == 255;
		}

		private static bool IsEmpty( Color color )
		{
			return color.A == 0 && color.R==0 && color.G == 0 && color.B == 0;
		}

		protected internal static string GetColorName( int r, int g, int b )
		{
			foreach( Color c in _webColors)
				if( c.R == r && c.G== g && c.B== b) return c.Name;

			foreach( Color c in _systemColors)
				if( c.R == r && c.G== g && c.B== b) return c.Name;

			return UNKNOWN_COLOR_TEXT;
		}


		private static string GetColorName( int r, int g, int b, int a )
		{
			if( a == 0 && r==255 && g == 255 && b == 255) return "Transparent";
			if( a == 0 && r==0 && g == 0 && b == 0) return "Empty";
 
			string colorName = GetColorName( r, g, b );

			if( colorName == UNKNOWN_COLOR_TEXT ) return UNKNOWN_COLOR_TEXT;

			if( a == 255 ) return colorName;

			string percentChar = CultureInfo.CurrentCulture.NumberFormat.PercentSymbol;
			int tx = (int)System.Math.Round( (1.0f-((float)a/255.0f))*100f);
			return string.Format(null,"{0}; {1}{2}",colorName, tx, percentChar);

		}

		protected internal static string ToColorString( Color color )
		{
			if( IsEmpty( color )) return "Empty";

			string percentChar = CultureInfo.CurrentCulture.NumberFormat.PercentSymbol;

			int tx = (int)System.Math.Round( (1.0f-((float)color.A/255.0f))*100f);

			if( color.IsNamedColor ) return string.Format(null,"{0}; {1}{2}",color.Name, tx, percentChar);

			string name = GetColorName( color.R,color.G,color.B,color.A );

			if( name == UNKNOWN_COLOR_TEXT) 
				return string.Format(null,"{0}; {1}; {2}; {3}{4}",color.R, color.G, color.B,tx, percentChar);
			else//return string.Format(null,"{0}; {1}{2}",name,tx, percentChar);
				return name;

		}

		protected internal static Color FromColorString( string colorString )
		{
			CultureInfo ci = CultureInfo.CurrentCulture;

			if( colorString.Length == 0 || string.Compare( colorString, "empty", true, ci)== 0 )
				return Color.Empty;
				
			string[] args = colorString.Split(new char[]{';'});
			
			Color c = Color.FromName(args[0]);

			char[] percentChar = ci.NumberFormat.PercentSymbol.ToCharArray();


			int a		= 255;

			if(! c.IsNamedColor ) 
			{

				int r		= int.Parse( args[0].Trim() );
				int g		= int.Parse( args[1].Trim() );
				int b		= int.Parse( args[2].Trim() );

				if( args.Length >= 4 )
				{
					string stx		= args[3].Trim();	
					string[] _stx	= stx.Split(percentChar);	
					stx				= _stx[0];

					float tx	= float.Parse( stx );
					if( tx > 100f)	tx = 100f;
					if( tx < 0f)	tx = 0f;
					tx/=100f;
					a = (int)System.Math.Round(255.0f - (tx*255.0f));
				}

				string name = GetColorName( r,g,b, c.A );

				if( name == UNKNOWN_COLOR_TEXT ) return Color.FromArgb(a, r,g,b);
				else return Color.FromName( name );
			}
			else 
			{
				if( args.Length >= 2 )
				{
					string stx		= args[1].Trim();	
					string[] _stx	= stx.Split(percentChar);	
					stx				= _stx[0];

					float tx	= float.Parse( stx );
					if( tx > 100f)	tx = 100f;
					if( tx < 0f)	tx = 0f;
					tx/=100f;
					a = (int)System.Math.Round(255.0f - (tx*255.0f));
				}

				if( a == 255) return c ;
				else return Color.FromArgb(a, c);
			}


		}


		/// <summary>
		/// The class _ColorSorter orders the colors based on the hue, saturation and brightness. This is the
		/// order that is also used by visual studio.
		/// </summary>
		private class ColorSorter: System.Collections.IComparer 
		{

			#region IComparer Members

			public int Compare (object x, object y) 
			{
				// local variables
				Color					cx, cy;
				float					hx, hy, sx, sy, bx, by;

				// get Color values
				cx = (Color) x;
				cy = (Color) y;
				// get saturation values
				sx = cx.GetSaturation ();
				sy = cy.GetSaturation ();
				// get hue values
				hx = cx.GetHue ();
				hy = cy.GetHue ();
				// get brightness values
				bx = cx.GetBrightness ();
				by = cy.GetBrightness ();

				// determine order
				// 1 : hue       
				if (hx < hy) return -1; 
				else if (hx > hy) return 1;
				else 
				{
					// 2 : saturation
					if (sx < sy) return -1;
					else if (sx > sy) return 1;
					else 
					{
						// 3 : brightness
						if (bx < by) return -1;
						else if (bx > by) return 1;
						else return 0;
					}
				}
			}

			#endregion

		}



	}

	

	public class TransparentColorConverter:ExtendedColorConverter
	{
		//Do not use the extended color Converter for the same type...
		//Use this for transparent color converter
		public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
		{
			if( value is string)
				return new TransparentColor( FromColorString( (string)value) );

			return base.ConvertFrom (context, culture, value);
		}


	}



}
